package com.such.kit.jna;

import java.util.ArrayList;
import java.util.List;
import org.xvolks.jnative.JNative;
import org.xvolks.jnative.Type;
import org.xvolks.jnative.exceptions.NativeException;
import org.xvolks.jnative.pointers.Pointer;
import com.jacob.activeX.ActiveXComponent;
import com.jacob.com.Dispatch;
import com.jacob.com.LibraryLoader;
import com.jacob.com.Variant;
import com.such.kit.jna.bean.JNativeArg;
import com.such.kit.string.StringCommons;
import com.such.kit.validate.ValidateCommons;

/**
 * @author SUCH
 * <pre>
 * DLL、COM 调用工具类
 * 目前提供两类调用实现，分别是基于 JNative、Jacob 实现
 * 由于 DLL、COM 对运行环境要求较高，不同的实现都有相应限制，具体参考各方法中的说明
 * </pre>
 */
public class JNACommons {

	/**
	 * <pre>
	 * 基于 JNative 实现的 DLL 调用
	 * 
	 * [警告] JNative 只能调用 C / C++ 编写的 dll 库，由于 JNativeCpp.dll 只有 32 位版本，因此只能在 32 位 JDK 下运行
	 * </pre>
	 * @param envDLL JNativeCpp.dll 完整路径
	 * @param libDLL 需要加载的 dll 库
	 * @param method 需要调用的方法
	 * @param args 参数集
	 * @return 执行结果
	 * @throws NativeException
	 * @throws IllegalAccessException
	 */
	public static String invokeByJNative(String envDLL, String libDLL, String method, JNativeArg... args) 
			throws NativeException, IllegalAccessException {
		String result = "";
		List<Pointer> inPointerList = new ArrayList<Pointer>();
		Pointer outPointer = null;
		try {
			// 1. 加载 JNativeCpp.dll
			System.setProperty("jnative.loadNative", envDLL);
			// 2. 加载动态库并指定调用方法
			JNative jNative = new JNative(libDLL, method);
			// 3. 设置入参及返回类型
			int index = 0;
			boolean needOut = false;
			if (ValidateCommons.isNotEmpty(args)) {
				for (JNativeArg arg : args) {
					String ioType = arg.getIOType();
					Type type = arg.getType();
					String value = arg.getValue();
					int valueSize = arg.getValueSize();
					if (JNativeArg.IO_TYPE_IN.equals(ioType)) {
						if (Type.PSTRUCT.equals(type)) {
							byte[] valueBytes = value.getBytes();
							Pointer pointer = Pointer.createPointer(valueSize);
							pointer.setMemory(valueBytes);
							jNative.setParameter(index, pointer);
							inPointerList.add(pointer);
						} else {
							jNative.setParameter(index, type, value);
						}
						index++;
					} else if (JNativeArg.IO_TYPE_OUT.equals(ioType)) {
						needOut = true;
						if (Type.PSTRUCT.equals(type)) {
							outPointer = Pointer.createPointer(valueSize);
							jNative.setParameter(index, outPointer);
							index++;
						} else {
							jNative.setRetVal(type);
						}
					}
				}
			}
			// 4. 执行
			jNative.invoke();
			// 5. 获取返回值
			if (needOut) {
				if (ValidateCommons.isNotEmpty(outPointer)) {
					result = outPointer.getAsString();
				} else {
					result = jNative.getRetVal();
				}
			}
		} finally {
			if (ValidateCommons.isNotEmpty(inPointerList)) {
				for (Pointer inPointer : inPointerList) {
					inPointer.dispose();
				}
			}
			if (ValidateCommons.isNotEmpty(outPointer)) {
				outPointer.dispose();
			}
		}
		return result;
	}

	/**
	 * <pre>
	 * 基于 Jacob 实现的 COM 调用
	 * 
	 * [警告] Jacob 只能调用 COM 组件库，Jacob 高度依赖 Windows，要求 COM 在注册表中进行注册，根据注册名找到 COM
	 * </pre>
	 * @param envDLL 运行环境 dll 所在目录，目录中应包含 32、64 位 dll 文件，系统会根据 JDK 版本自动加载
	 * @param programId 需要加载的 dll 注册名
	 * @param method 需要调用的方法
	 * @param args 参数集
	 * @return 执行结果
	 */
	public static String invokeByJacob(String envDLL, String programId, String method, Object... args) {
		System.setProperty(LibraryLoader.JACOB_DLL_PATH, StringCommons.merger(envDLL, "/", LibraryLoader.getPreferredDLLName(), ".dll"));
		ActiveXComponent component = new ActiveXComponent(programId);
		Variant variant = Dispatch.call(component, method, args);
		return variant.getString();
	}

}
